package com.qd.system.importexcel.dict;

import com.alibaba.excel.read.metadata.holder.ReadRowHolder;
import com.cdqidi.dto.BatchResult;
import com.cdqidi.excel.domain.ExcelRowMsg;
import com.cdqidi.excel.domain.ImportErr;
import com.cdqidi.excel.util.ExcelUtil;
import com.cdqidi.exception.ExcelOperatorException;
import com.cdqidi.util.ValidatorUtil;
import com.cdqidi.valid.group.DefaultGroup;
import com.qd.common.sys.domain.entity.dict.DictDataDTO;
import com.qd.system.model.DictData;
import com.qd.system.service.dict.DictDataService;
import com.qd.system.service.dict.DictTypeService;
import com.qd.system.service.dict.MbUtil;
import org.springframework.util.StringUtils;

import javax.validation.ConstraintViolationException;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;

import static java.util.stream.Collectors.toList;

/**
 * 数据字典导入处理类
 *
 * @author sjk
 */
public class DictDataImportHandler {
    private final DictDataService dictDataService;
    private final DictTypeService dictTypeService;
    private final ValidatorUtil validatorUtil;
    private final List<ImportErr> errorList;
    private final LocalDateTime now = LocalDateTime.now();

    public DictDataImportHandler(DictDataService dictDataService, DictTypeService dictTypeService, ValidatorUtil validatorUtil, List<ImportErr> errorList) {
        this.dictDataService = dictDataService;
        this.dictTypeService = dictTypeService;
        this.validatorUtil = validatorUtil;
        this.errorList = errorList;
    }

    /**
     * 处理具体导入
     *
     * @param rowList excel行数据集合，默认为1000行
     * @param results 异常信息存储
     */
    public void process(List<ExcelRowMsg<DictDataDTO>> rowList, List<BatchResult> results) {
        final List<DictDataDTO> dictDataList = rowList.stream().map(ExcelRowMsg::getRowData).collect(toList());
        final List<String> existsIds = existsIds(dictDataList);
        final int size = rowList.size();
        try {
            for (ExcelRowMsg<DictDataDTO> excelRowMsg : rowList) {
                final DictDataDTO DictDataDTO = excelRowMsg.getRowData();
                final ReadRowHolder readRowHolder = excelRowMsg.getReadRowHolder();
                final List<String> errors = buildImportDto(DictDataDTO);
                if (!errors.isEmpty()) {
                    errorList.add(ExcelUtil.buildError(readRowHolder, errors));
                }
            }
            if (!errorList.isEmpty()) {
                throw new ExcelOperatorException(errorList);
            } else {
                final List<DictData> addDictDataList = new ArrayList<>(size);
                final List<DictData> updateDictDataList = new ArrayList<>(size);
                doProcessDictData(existsIds, dictDataList, addDictDataList, updateDictDataList);
                BatchResult.buildBatchResult(results, addDictDataList.size(), updateDictDataList.size());
                addDictDataList.clear();
                updateDictDataList.clear();
            }
        } finally {
            dictDataList.clear();
            existsIds.clear();
        }
    }

    /**
     * 保存数据
     *
     * @param existsIds          已经存在的数据库主键
     * @param dictDataList       excel行数据集合
     * @param addDictDataList    添加集合
     * @param updateDictDataList 更新集合
     */
    private void doProcessDictData(List<String> existsIds, List<DictDataDTO> dictDataList, List<DictData> addDictDataList, List<DictData> updateDictDataList) {
        dictDataList.forEach(dictDataDTO -> {
            final DictData dictData = dictDataService.dtoToModel(dictDataDTO);
            dictDataService.clearCache(dictData);
            if (MbUtil.isExistInCollection(existsIds, dictDataDTO.getId())) {
                dictData.setAddTime(null);
                dictData.setUpdateTime(now);
                updateDictDataList.add(dictData);
            } else {
                dictData.setAddTime(now);
                dictData.setUpdateTime(null);
                addDictDataList.add(dictData);
            }
        });
        if (!addDictDataList.isEmpty()) {
            dictDataService.saveBatch(addDictDataList);
        }
        if (!updateDictDataList.isEmpty()) {
            dictDataService.updateBatchIds(updateDictDataList);
        }
    }

    /**
     * 获得主键存在的集合
     *
     * @param dictDataList excel行数据集合
     * @return 主键集合
     */
    private List<String> existsIds(List<DictDataDTO> dictDataList) {
        final List<String> ids = dictDataList.stream().map(DictDataDTO::getId).collect(toList());
        final List<String> existsIds = dictDataService.getBaseMapper().getIds(ids);
        ids.clear();
        return existsIds;
    }

    /**
     * 导入验证
     *
     * @param DictDataDTO 单个excel行数据
     * @return 异常信息
     */
    private List<String> buildImportDto(DictDataDTO DictDataDTO) {
        final List<String> errors = new ArrayList<>();
        final Integer status = MbUtil.isStatusValidate(errors, DictDataDTO.getStatusDisplay());
        if (null != status) {
            DictDataDTO.setStatus(status);
        }
        final String typeIdDisplay = DictDataDTO.getTypeIdDisplay();
        final String typeId = dictTypeService.isTypeIdValidate(typeIdDisplay, errors);
        if (StringUtils.hasLength(typeId)) {
            DictDataDTO.setTypeId(typeId);
        }
        importValidate(DictDataDTO, errors);
        return errors;
    }

    /**
     * 导入实体验证
     *
     * @param DictDataDTO 单个excel行数据
     * @param errors      异常信息存储
     */
    private void importValidate(DictDataDTO DictDataDTO, List<String> errors) {
        if (errors.isEmpty()) {
            try {
                validatorUtil.checkValid(DictDataDTO, DefaultGroup.class);
            } catch (ConstraintViolationException e) {
                errors.addAll(validatorUtil.getValidationError(e));
            }
        }
    }
}
